use proc_macro::TokenStream;
use quote;
use syn::{ spanned::Spanned, DeriveInput };
// attributes(builder) 追加为惰性属性
#[proc_macro_derive(Builder, attributes(builder))]
pub fn derive(input: TokenStream) -> TokenStream {
    let st = syn::parse_macro_input!(input as syn::DeriveInput);
    match do_expand(&st) {
        Ok(token) => token.into(),
        Err(e) => e.to_compile_error().into(),
    }
}

type StructFields = syn::punctuated::Punctuated<syn::Field, syn::Token![,]>;

type TokenStreamResult = syn::Result<proc_macro2::TokenStream>;

fn get_fields_from_derive_input(st: &syn::DeriveInput) -> syn::Result<&StructFields> {
    if
        let syn::Data::Struct(
            syn::DataStruct { fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }), .. },
        ) = st.data
    {
        return Ok(named);
    }
    Err(syn::Error::new_spanned(st, "Must Define On Struct, Not on Enum".to_string()))
}
fn get_generic_inner_type<'a>(ty: &'a syn::Type, outer_ident_name: &str) -> Option<&'a syn::Type> {
    if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = ty {
        if let Some(seg) = segments.last() {
            if seg.ident.to_string() == outer_ident_name {
                eprintln!("outer_ident_name = {:#?}", outer_ident_name);
                if
                    let syn::PathArguments::AngleBracketed(
                        syn::AngleBracketedGenericArguments { ref args, .. },
                    ) = &seg.arguments
                {
                    eprintln!("inner_type = {:#?}", args);
                    if let Some(syn::GenericArgument::Type(inner_type)) = args.first() {
                        eprintln!("inner_type = {:#?}", inner_type);
                        return Some(inner_type);
                    }
                }
            }
        }
    }

    // if let syn::Type::Path(syn::TypePath{ ref path,..}) = ty{
    //         if let Some(seg) = path.segments.last() {
    //             if seg.ident.to_string() == "Option"{
    //                 if let syn::PathArguments::AngleBracketed(
    //                     syn::AngleBracketedGenericArguments{ref args, ..},
    //                 ) = seg.arguments{
    //                 if let Some(syn::GenericArgument::Type(inner_type)) = args.first(){
    //                     return Some(inner_type);
    //                 }
    //                 }
    //             }
    //         }
    //     }
    None
}

fn generate_builder_struct_fields_def(st: &DeriveInput) -> TokenStreamResult {
    let fields = get_fields_from_derive_input(st)?;
    let idents: Vec<_> = fields
        .iter()
        .map(|f| &f.ident)
        .collect();
    let types: syn::Result<Vec<_>> = fields
        .iter()
        .map(|f| {
            if let Some(inner_type) = get_generic_inner_type(&f.ty, "Option") {
                Ok(quote::quote!(
                    std::option::Option<#inner_type>
                ))
            } else if get_user_specified_for_vec(f)?.is_some() {
                let original_type = &f.ty;
                Ok(quote::quote!(#original_type)) 
            } else {
                let origin_type = &f.ty;
                Ok(quote::quote!(std::option::Option<#origin_type>))
            }
        })
        .collect();

        let types = types?;

    let res = quote::quote!(
        #(#idents: #types), *
    );
    return Ok(res);
}

fn generate_builder_struct_factory_init_clauses(
    st: &DeriveInput
) -> syn::Result<Vec<proc_macro2::TokenStream>> {
    let fields = get_fields_from_derive_input(st)?;
    let init_clauses: syn::Result<Vec<_>> = fields
        .iter()
        .map(|f| {
            let ident = &f.ident;
            if get_user_specified_for_vec(&f)?.is_some() {
               Ok(quote::quote!(#ident: std::vec::Vec::new()))
            } else {
                Ok(quote::quote!(#ident: std::option::Option::None))
            }
        })
        .collect();
    init_clauses
}

fn generate_setter_functions(st: &DeriveInput) -> TokenStreamResult {
    let fields = get_fields_from_derive_input(st)?;
    let idents: Vec<_> = fields
        .iter()
        .map(|f| &f.ident)
        .collect();
    let types: Vec<_> = fields
        .iter()
        .map(|f| &f.ty)
        .collect();
    let mut final_token_stream = proc_macro2::TokenStream::new();

    for (idx, (ident, type_)) in idents.iter().zip(types.iter()).enumerate() {
        let token_stream_piece = if let Some(inner_type) = get_generic_inner_type(type_, "Option") {
            quote::quote!(
                fn #ident(&mut self, #ident: #inner_type) -> &mut Self {
                    self.#ident = std::option::Option::Some(#ident);
                    self
                }
            )
        } else if let Some(ref user_specified_ident) = get_user_specified_for_vec(&fields[idx])? {
            let inner_type = get_generic_inner_type(type_, "Vec").ok_or(
                syn::Error::new(fields[idx].span(), "`each` field must be a Vec type")
            )?;

            let mut t_token_stream_piece =
                quote::quote!(
                    fn #user_specified_ident(&mut self, #user_specified_ident: #inner_type) -> &mut Self{
                        self.#ident.push(#user_specified_ident);
                        self
                    }
                );

            if user_specified_ident != ident.as_ref().unwrap() {
                t_token_stream_piece.extend(
                    quote::quote!(
                        fn #ident(&mut self, #ident: #type_) -> &mut Self {
                            self.#ident =  #ident.clone();
                            self
                        }
                    )
                );
            }
            t_token_stream_piece
        } else {
            quote::quote!(
                fn #ident(&mut self, #ident: #type_) -> &mut Self {
                    self.#ident = std::option::Option::Some(#ident);
                    self
                }
            )
        };
        final_token_stream.extend(token_stream_piece);
    }
    Ok(final_token_stream)
}

fn generate_build_function(st: &syn::DeriveInput) -> TokenStreamResult {
    let fields = get_fields_from_derive_input(st)?;
    let st_name = &st.ident;
    let mut checker_code_pieces = Vec::new();
    let mut entry_fields = Vec::new();
    for field in fields {
        let ident = &field.to_owned().ident;
        // if get_user_specified_for_vec(field).is_some() {
        //       entry_fields.push(
        //         quote::quote!(
        //         #ident: self.#ident.clone()
        //     ));
        // } else
        if
            get_generic_inner_type(&field.ty, "Option").is_none() &&
            get_user_specified_for_vec(field)?.is_none()
        {
            entry_fields.push(
                quote::quote!(
                #ident: self.#ident.clone().unwrap()
            )
            );
            checker_code_pieces.push(
                quote::quote!(
                    if self.#ident.is_none() {
                        return std::result::Result::Err(format!("{} field is missing", stringify!(#ident)).into());
                    }
                )
            );
        } else {
            entry_fields.push(
                quote::quote!(
                #ident: self.#ident.clone()
            )
            );
        }
    }

    Ok(
        quote::quote!(
        pub fn build(&mut self) ->
        std::result::Result<#st_name,
         std::boxed::Box<dyn 
         std::error::Error>> {
            #(#checker_code_pieces)*
           Ok(#st_name{#(#entry_fields),* })
        }
    )
    )
}

fn get_user_specified_for_vec(field: &syn::Field) -> syn::Result<Option<syn::Ident>>  {
    for attr in &field.attrs {
        if let Ok(syn::Meta::List(syn::MetaList { ref path, ref nested, .. })) = attr.parse_meta() {
            if let Some(p) = path.segments.first() {
                if p.ident.to_string() == "builder" {
                    if let Some(syn::NestedMeta::Meta(syn::Meta::NameValue(kv))) = nested.first() {
                        eprintln!("kv.path = {:#?}", &kv.path);
                        if kv.path.is_ident("each") {
                            if let syn::Lit::Str(ref ident_str) = kv.lit {
                                eprintln!("kv.path = {:#?}", &ident_str);
                                return Ok(Some(
                                    syn::Ident::new(ident_str.value().as_str(), attr.span())
                                ));
                            }
                        }else {
                            if let Ok(syn::Meta::List(ref list)) = attr.parse_meta() {
                                return Err(syn::Error::new_spanned(list,r#"expected `builder(each = "...")`"#));
                            }
                        }
                    }
                }
            }
        }
    }
    Ok(None)
}

fn do_expand(st: &DeriveInput) -> TokenStreamResult {
    eprintln!("do_token st = {:#?}", st);
    let st_name = st.ident.to_string();
    let builder_name = format!("{}Builder", st_name);
    let builder_name_ident = syn::Ident::new(&builder_name, st.span());

    let st_ident = &st.ident;

    let builder_struct_field_def = generate_builder_struct_fields_def(st)?;
    let builder_struct_factory_init_clauses = generate_builder_struct_factory_init_clauses(st)?;
    let setter_functions = generate_setter_functions(st)?;
    let entry_builder = generate_build_function(st)?;

    let ret =
        quote::quote!(
    pub struct #builder_name_ident {
        #builder_struct_field_def
    }
    impl #st_ident {
        pub fn builder() -> #builder_name_ident {
            #builder_name_ident {
                #(#builder_struct_factory_init_clauses),*
            }
        }
    }
    impl #builder_name_ident {
            #setter_functions

            #entry_builder
     }
    );
    eprintln!("{}", ret);
    Ok(ret)
}

#[proc_macro_derive(ExploreAttribute)]
pub fn attribute_explore(input: TokenStream) -> TokenStream {
    let st = syn::parse_macro_input!(input as syn::DeriveInput);
    let attr = st.attrs.first().unwrap();
    eprintln!("{:#?}", attr);
    proc_macro2::TokenStream::new().into()
}